#include "boot_loader.h"
#include "game_application.h"
#include "graphics/graphics_util.h"
#include "graphics/avi.h"

#include "dd/dd_man.h"
#include "font/font_manager.h"
#include "script/scripthelper.h"

#include "util/timer.h"		    // Needs to come in early
#include "util/mmgr.h"		    // Needs to come in early
#include "util/common_macros.h"		// macros
#include "util/log.h"		    // logging

using namespace LuaPlus;

BootLoader::BootLoader(bool AllowSkip) :
	input::KeyboardListener(),input::MouseListener(),
	m_CurrentIndex(-1),
	m_IsDone(false),
	m_SpeedSkip(false),
	m_AllowSkip(AllowSkip),
	m_HasSkipped(false)

{
	VISTA_SKIP_BUG = true;
}

BootLoader::~BootLoader()
{
	for (unsigned int i=0; i<m_Pages.size(); i++)
	{
		if (m_Pages[i]->pVideo)
			GetGraphics()->DestroyMovie(m_Pages[i]->pVideo);
		
		if (m_Pages[i]->Sound)
			AUDIO().StopSound(m_Pages[i]->Sound);
		
		delete m_Pages[i];
	}
	m_Pages.resize(0);
}

void			BootLoader::Tick()
{
	TIMER().Tick();
	float dt = TIMER().Delta();

	if (m_CurrentIndex < 0)	GoState(0);	// transition to state
	if (IsDone()) return;

	if (!m_FadeIn.IsReady())	
	{	
		m_FadeIn.Tick(dt); 
		if (m_FadeIn.IsReady() && !m_Pages[m_CurrentIndex]->PlaySoundEvent.empty())	// just finished
		{
			m_Pages[m_CurrentIndex]->Sound = AUDIO().PlayEvent(m_Pages[m_CurrentIndex]->PlaySoundEvent);
		}
	} else
	if (!m_Stay.IsReady())		{	m_Stay.Tick(dt); } else
	if (!m_FadeOut.IsReady())	{	m_FadeOut.Tick(dt); } else
								{	GoState(m_CurrentIndex+1); }

	if (m_CurrentIndex >= 0 && m_CurrentIndex < (int)m_Pages.size())
	{
		Page* pPage  = m_Pages[m_CurrentIndex];
		if (pPage->pVideo)
		{
			if (m_FadeIn.IsReady() && !m_Stay.IsReady())		
			{
				pPage->pVideo->Tick();
			}

		} else
		{
			pPage->Sprite.Tick(dt);
		}	
	}
	g_pApp->GetHandler()->UpdateOS();
#ifdef _PLATFORM_WIN32
	if (VISTA_SKIP_BUG)
	{
		// default message processing to avoid not responding bug in Vista when skipping
		MSG  msg;
		if ( ::PeekMessage(&msg, NULL, 0, 0, PM_REMOVE) )
		{
			if (m_AllowSkip)
			{
				// temporary solution for Win32 to allow skipping of splash screens
				switch (msg.message)
				{
				case WM_LBUTTONUP:
				case WM_RBUTTONUP:
				case WM_MBUTTONUP:
				case WM_KEYUP:
					SpeedupTransition();
					break;
				}
			}
			::DefWindowProc(msg.hwnd, msg.message, msg.wParam, msg.lParam);
		}
	}
#endif //_PLATFORM_WIN32
}

void			BootLoader::Render()
{
	if (IsDone() || !ValidState(m_CurrentIndex)) return;
	float	Alpha = 1.0f;
	if (!m_FadeIn.IsReady())	{	Alpha = m_FadeIn.GetProgress();	} else	// fade in
	if (!m_Stay.IsReady())		{	} else									// do nothing 
	if (!m_FadeOut.IsReady())	{	Alpha = 1.0f - m_FadeOut.GetProgress(); }// fade out
		
	if (m_FadeOut.IsReady()) return;


	bool	UseFilter = GetGraphics()->GetFilter2D();
	GetGraphics()->SetFilter2D(true);
	GetGraphics()->Start2D();
	GetGraphics()->Clear(0x00);

	// ADRIAN: make this section more robust
	if (GetGraphics()->BeginScene())
	{
		// Render sprite
		if (m_CurrentIndex>=0 && m_CurrentIndex < (int)m_Pages.size())
		{
			COLOR	c = ColorfRGBA(1, 1, 1, Alpha);
			Page* pPage  = m_Pages[m_CurrentIndex];
			if (!pPage->pVideo)
			{			
				pPage->Sprite.Setup(	math::Vec2(0,0), 
														math::Vec2(1,1), 
														0,
														1,
														c);	
				pPage->Sprite.m_Pos = math::Vec2(0.5f,0.5f);
				pPage->Sprite.m_Size.y *= GraphicsUtil::W2H;
				pPage->Sprite.RenderRotated();
			} else
			if (pPage->pVideo)
			{	
				int	ScaledHeight = GetGraphics()->GetScreenHeight();
				int	ScaledWidth  = int(pPage->pVideo->GetWidth() * ((float)GetGraphics()->GetScreenHeight()/(float)pPage->pVideo->GetHeight()));
				int	OffsetX = (GetGraphics()->GetScreenWidth() - ScaledWidth) / 2;
				int	OffsetY = (GetGraphics()->GetScreenHeight() -ScaledHeight) / 2;

				Graphics::Vertex2D	VertA[6];
				GraphicsUtil::FillQuad(
					math::Vec2(OffsetX * GraphicsUtil::InvW,OffsetY* GraphicsUtil::InvH), 
					math::Vec2(ScaledWidth * GraphicsUtil::InvW ,ScaledHeight * GraphicsUtil::InvH), math::Vec2(0,1), math::Vec2(1,0), c, VertA);
				pPage->pVideo->SetTextureStage(0);
				GetGraphics()->DrawVertex2D(VertA, 6); 
			}
		}
	//	FontManager::Instance().RenderCache();
		dd::Manager::Instance().Render2D();

		GetGraphics()->EndScene();
		GetGraphics()->Present();
	}
	else
	{
	//	_LOG(MSG_ERROR, "Bootloader: Splash screen failed to render");
	}

	GetGraphics()->SetFilter2D(UseFilter);
}

void			BootLoader::Load(const std::string& File)
{
	using namespace script;

	LuaPlus::LuaState*	pScript = ScriptHelper::CreateLuaState();
	if (!ScriptHelper::DoFile(*pScript, File))
	{
		_LOG(MSG_ERROR, "Unable to load bootloader files from file " << File << "!");
		 LuaState::Destroy( pScript );
		return;
	} else
	{
		LuaPlus::LuaObject	Pages = ScriptHelper::GetObj(*pScript, "Pages");
		if (Pages.IsTable())
		{
			int PageC = Pages.GetTableCount();
			for (int i=0; i<PageC; i++)
			{
				LuaPlus::LuaObject	Data = Pages.GetByIndex(i+1);
				if (Data.IsTable())
				{
					std::string Image		= ScriptHelper::GetTableString(Data, "Image");
					std::string Video		= ScriptHelper::GetTableString(Data, "Video");
					std::string Sound		= "";
					bool IsVideo			= !Video.empty();

					if (!Image.empty() || IsVideo)
					{
						float StayTime		= ScriptHelper::GetTableFloat(Data, "StayTime", 3.0f);
						float FadeIn		= ScriptHelper::GetTableFloat(Data, "FadeIn", 1.0f);
						float FadeOut		= ScriptHelper::GetTableFloat(Data, "FadeOut", 1.0f);
						bool AllowSkip		= ScriptHelper::GetTableBool(Data, "AllowSkip", true);

						if (IsVideo)
							Sound			= ScriptHelper::GetTableString(Data, "Sound");

						AddPage( IsVideo ? Video : Image, StayTime, FadeIn, FadeOut, IsVideo, Sound, AllowSkip );
					}
				}
			}
		}
	}
	LuaState::Destroy( pScript );
}

bool			BootLoader::AddPage(const std::string& Image, float StayTime , float FadeIn , float FadeOut, bool IsAvi, const std::string& PlaySoundEvent, bool AllowSkip )
{

	Page*	NewPage = new Page();
	NewPage->AllowSkip = AllowSkip;
	//NewPage->Sprite.Create(Image, math::Vec2(0,0), math::Vec2(800.0f/1024.0f, 600.0f/1024.0f));
	if (!IsAvi)
	{
		NewPage->Sprite.Create(Image, math::Vec2(0.0f, 0.0f));
	} else
	{
		NewPage->pVideo = GetGraphics()->LoadMovie(Image);
		if (NewPage->pVideo)
		{
			StayTime = NewPage->pVideo->GetRunTime();
		}	
	}
	if (NewPage->Sprite.IsValid() || NewPage->pVideo)
	{
		if (NewPage->Sprite.GetImage())
		{
//#ifdef _PLATFORM_IOS
			
			NewPage->Sprite.m_UV2 =math::Vec2( 1.0f,
									1.0f );
//#else
//			NewPage->Sprite.m_UV2 = math::Vec2( (float)GraphicsUtil::WIDTH / (float)NewPage->Sprite.GetImage()->GetWidth(),
//												(float)GraphicsUtil::HEIGHT / (float)NewPage->Sprite.GetImage()->GetHeight() );
//#endif
		}
		else if (NewPage->Sprite.GetSpriteInst())
		{
			
			NewPage->Sprite.m_UV2 = math::Vec2( (float)GraphicsUtil::WIDTH / (float)NewPage->Sprite.GetSpriteInst()->pImage->GetWidth(),
												(float)GraphicsUtil::HEIGHT / (float)NewPage->Sprite.GetSpriteInst()->pImage->GetHeight() );
		}
		NewPage->PlaySoundEvent = PlaySoundEvent;
		NewPage->FadeIn = FadeIn;
		NewPage->FadeOut = FadeOut;
		NewPage->StayTime = StayTime;
		m_Pages.push_back(NewPage);
	} else
	{
		delete NewPage;
		return false;
	}
	return true;
}

bool			BootLoader::ValidState(int Index)
{
	return Index < (int)m_Pages.size();
}

void			BootLoader::GoState(int Index)
{
	if (!ValidState(Index))
	{		
		m_IsDone = true;
		return;
	}	
	m_CurrentIndex = Index;
	m_FadeIn.SetLimit(m_Pages[m_CurrentIndex]->FadeIn);	
	m_Stay.SetLimit(m_Pages[m_CurrentIndex]->StayTime);	
	m_FadeOut.SetLimit(m_Pages[m_CurrentIndex]->FadeOut);	

	m_FadeIn.Reset();
	m_Stay.Reset();
	m_FadeOut.Reset();
	m_HasSkipped = false;
}

void			BootLoader::SpeedupTransition()
{
	if (m_HasSkipped) return; // already skipping
	if (ValidState(m_CurrentIndex) && !m_Pages[m_CurrentIndex]->AllowSkip) return; // this page cannot be skipped

	if (m_SpeedSkip)
	{
		m_FadeOut.ForceReady();
	} else
	{
		if (!m_FadeIn.IsReady())
		{
			m_FadeOut.SetCounter((m_FadeIn.GetProgress()) * m_FadeOut.GetLimit());
		} 
	}
	m_FadeIn.ForceReady();
	m_Stay.ForceReady();
	m_HasSkipped = true;
}